package nil.ed.livechat.timeshift.utils.m3u8;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * @author delin10
 * @since 2020/6/25
 **/
public class MpegTsUtils {
    static CountDownLatch latch = new CountDownLatch(55);

//    private static String FFMPEG_PATH = "D:\\000delin\\dev\\ffmpeg-3.3.1-win64-static\\bin\\ffmpeg.exe";
    private static String FFMPEG_PATH = "/usr/local/bin/ffmpeg";
    public static void main(String[] args) throws InterruptedException, IOException {
        List<String> ls = new LinkedList<>();
        for (int i = 0; i < 55; i++) {
            ls.add("D:/ts/1-" + i + ".ts");
        }
        System.out.println(index(ls, 100L));
    }

    public static String formatOffset(long secs) {
        long modSec = secs % 60;
        long minutes = secs / 60;
        long modMinutes = minutes % 60;
        long hours = minutes / 60;
        return String.format("%02d:%02d:%02d", modSec, modMinutes, hours);

    }

    public static double parseDuration(String durationStr) {
        String[] segs = durationStr.split(":");
        double hour = Double.parseDouble(segs[0]);
        double minutes = Double.parseDouble(segs[1]);
        double secs = Double.parseDouble(segs[2]);

        return secs + hour * 60 * 60 + minutes * 60;
    }

    public static boolean cutTs(String tsFile, String outFile, long sec) {
        try {
            List<String> cmds = new ArrayList<>();
            cmds.add(FFMPEG_PATH);
            cmds.add("-i");
            cmds.add(tsFile);
            cmds.add("-ss");
            cmds.add(formatOffset(sec));
            cmds.add(outFile);
            Runtime.getRuntime().exec(cmds.toArray(new String[0]));
            File f = new File(outFile);
            return f.exists();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }

    }

    private static final Pattern CAPTURE_TS_INFO = Pattern.compile("Duration\\s*:\\s*(?<duration>[0-9:.]*?)\\s*,\\s*start\\s*:\\s*(?<start>[0-9.]*?)\\s*,");
    public static List<TsInfo> resolve(List<String> files) throws IOException, InterruptedException {
        byte[] buf = new byte[1024];
        List<TsInfo> infos = new LinkedList<>();
        for (String f : files) {
            System.out.println(f);
            List<String> cmds = new ArrayList<>();
            cmds.add(FFMPEG_PATH);
            cmds.add("-i");
            cmds.add(f);
            Process process = Runtime.getRuntime().exec(cmds.toArray(new String[0]));
            List<String> lines = IOUtils.readLines(process.getErrorStream(), StandardCharsets.UTF_8);
            System.out.println(lines);
            String infoStr = String.join("\n", lines);
            TsInfo info = parseInfo(infoStr);
            infos.add(info);
        }
        return infos;
    }

    public static TsInfo getTsInfo(String f) throws IOException, InterruptedException {
        return resolve(Collections.singletonList(f)).get(0);
    }

    public static TsInfo index(List<String> files, long offset) throws IOException, InterruptedException {
        if (CollectionUtils.isEmpty(files)) {
            return null;
        }
        TsInfo info = getTsInfo(files.get(0));
        double duration = info.getDuration();

        if (offset < duration) {
            return info;
        } else if(files.size() == 1) {
            return null;
        }

        int predict = 1;
        while (predict < files.size()) {
            if (predict * duration > offset) {
                break;
            }
            predict++;
        }
        boolean forward = false;
        if (predict < files.size()) {
            info = getTsInfo(files.get(predict));
            if (info.start < offset) {
                if (info.start + duration > offset) {
                    return info;
                }
                forward = true;
            }
        }

        info = null;
        if (forward) {
            int i = predict + 1;
            for (; i < files.size(); ++i) {
                info = getTsInfo(files.get(i));
                if (info.start + duration > offset) {
                    return info;
                }
            }
            return null;
        } else {
            int i = predict - 1;
            for (; i >= 0; --i) {
                info = getTsInfo(files.get(i));
                if (info.start <= offset) {
                    return info;
                }
            }
        }
        return null;
    }

    public static TsInfo parseInfo(String output) {
        TsInfo info = new TsInfo();
        Matcher matcher = CAPTURE_TS_INFO.matcher(output);
        while (matcher.find()) {
            String v = matcher.group("duration");
            if (v != null) {
                info.duration = parseDuration(v);
            }

            v = matcher.group("start");
            if (v != null) {
                info.start = Double.parseDouble(v);
            }
        }
        return info;
    }

}
